package co.fitstart.admin.service.impl;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.demo2do.core.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dynamic;
import com.demo2do.core.support.Page;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;
import co.fitstart.admin.Constants;
import co.fitstart.admin.dao.CourseRoundDao;
import co.fitstart.admin.dao.CourseScheduleDao;
import co.fitstart.admin.dao.OrderDao;
import co.fitstart.admin.dao.PrincipalDao;
import co.fitstart.admin.dao.ReportDao;
import co.fitstart.admin.dao.UserDao;
import co.fitstart.admin.entity.search.AllOrderSearch;
import co.fitstart.admin.entity.search.OrderSearch;
import co.fitstart.admin.handler.SmsSender;
import co.fitstart.admin.handler.UserHandler;
import co.fitstart.admin.service.OrderService;
import co.fitstart.entity.Constant;
import co.fitstart.entity.coach.PrincipalReportRelation;
import co.fitstart.entity.coupon.Coupon;
import co.fitstart.entity.course.Course;
import co.fitstart.entity.course.CourseType;
import co.fitstart.entity.course.CourseUnit;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.round.CourseRoundAttribute;
import co.fitstart.entity.course.round.RoundStatus;
import co.fitstart.entity.course.schedule.ScheduleStatus;
import co.fitstart.entity.finance.ExchangeRate;
import co.fitstart.entity.finance.InvoiceExportRecord;
import co.fitstart.entity.finance.OrderInvoiceRecord;
import co.fitstart.entity.finance.PaidType;
import co.fitstart.entity.finance.Receipt;
import co.fitstart.entity.finance.ReceiptExportRecord;
import co.fitstart.entity.finance.ReceiptType;
import co.fitstart.entity.finance.SalesOrderExportRecord;
import co.fitstart.entity.order.Order;
import co.fitstart.entity.order.OrderAttribute;
import co.fitstart.entity.order.OrderInvestigateRecord;
import co.fitstart.entity.order.OrderPriceAttribute;
import co.fitstart.entity.order.OrderPriceType;
import co.fitstart.entity.order.OrderStatus;
import co.fitstart.entity.order.OrderType;
import co.fitstart.entity.order.PaymentMethod;
import co.fitstart.entity.order.PaymentStatus;
import co.fitstart.entity.order.RefundMethod;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.product.Product;
import co.fitstart.entity.reference.Contact;
import co.fitstart.entity.report.RevenueReport;
import co.fitstart.entity.report.RevenueType;
import co.fitstart.entity.site.Site;
import co.fitstart.entity.user.Student;
import co.fitstart.entity.user.User;
import co.fitstart.entity.user.UserSourceType;

/**
 * 
 * @author jiecheng
 *
 */
@Service("orderService")
public class OrderServiceImpl extends DataServiceImpl<Order> implements OrderService{

    private static final long WXC_ID = 6l;

    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Dynamic
    private CourseRoundDao courseRoundDao;
    
    @Dynamic
    private CourseScheduleDao courseScheduleDao;
    
    @Dynamic
    private OrderDao orderDao;
    
    @Dynamic
    private ReportDao reportDao;
    
    @Dynamic
    private PrincipalDao principalDao;
    
    @Autowired
    private UserHandler userHandler;
    
    @Value("#{config['file.upload.path']}")
    private String uploadPath;
    
    @Dynamic
    private UserDao userDao;
    
    @Autowired
    private SmsSender smsSender;
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.ecommerce.OrderService#list(co.fitstart.common.entity.ecommerce.order.OrderType, java.lang.String, com.demo2do.core.web.resolver.Page)
     */
    public List<Order> listByPage(OrderType orderType, String orderStatus, Page page) {
        
        List<OrderStatus> status = OrderStatus.sameAs(orderStatus);
        
        List<Order> result = orderDao.listByPage(orderType, status, page.getBeginIndex(), page.getMaxResultRecords());
        page.calculate(result.size());
        
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listByPage(boolean, boolean, java.lang.String, com.demo2do.core.support.Page)
     */
    public List<Order> listByPage(boolean experience, boolean give, String orderStatus, Page page) {
        List<OrderStatus> status = OrderStatus.sameAs(orderStatus);
        
        List<Order> result = orderDao.listByPage(OrderType.INDIVIDUAL, experience, give, status, page.getBeginIndex(), page.getMaxResultRecords());
        page.calculate(result.size());
        
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listByPage(co.fitstart.entity.site.Site, co.fitstart.entity.order.OrderType, java.lang.String, com.demo2do.core.support.Page)
     */
    public List<Order> listByPage(Site site, OrderType orderType, String orderStatus, Page page) {
        
        List<Order> result = null;
        
        List<OrderStatus> status = OrderStatus.sameAs(orderStatus);
        
        if(status == null)
            status = Arrays.asList(OrderStatus.aliasOf(orderStatus));  
        
        if(site != null) {
            result = orderDao.listByPage(site, orderType, status, page.getBeginIndex(), page.getMaxResultRecords());
        } else {
            result = orderDao.listByPage(orderType, status, page.getBeginIndex(), page.getMaxResultRecords());
        }
        
        page.calculate(result.size());
        
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listByPage(java.util.List, co.fitstart.entity.order.OrderType, java.lang.String, com.demo2do.core.support.Page)
     */
    public List<Order> listByPage(List<Site> sites, OrderType orderType, String orderStatus, Page page) {
        List<OrderStatus> status = OrderStatus.sameAs(orderStatus);
        
        if(status == null)
            status = Arrays.asList(OrderStatus.aliasOf(orderStatus));  
        
        List<Order> result = orderDao.listByPage(sites, orderType, status, page.getBeginIndex(), page.getMaxResultRecords());
        
        page.calculate(result.size());
        
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listByPage(co.fitstart.entity.site.Site, boolean, boolean, java.lang.String, com.demo2do.core.support.Page)
     */
    public List<Order> listByPage(Site site, boolean experience, boolean give, String orderStatus, Page page) {

        List<Order> result = null;
        
        List<OrderStatus> status = OrderStatus.sameAs(orderStatus);
        
        if(status == null)
            status = Arrays.asList(OrderStatus.aliasOf(orderStatus));  
        
        if(site != null) {
            result = orderDao.listByPage(site, OrderType.INDIVIDUAL, experience, give, status, page.getBeginIndex(), page.getMaxResultRecords());
        } else {
            result = orderDao.listByPage(OrderType.INDIVIDUAL, experience, give, status, page.getBeginIndex(), page.getMaxResultRecords());
        }
        
        page.calculate(result.size());
        
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listByPage(java.util.List, boolean, boolean, java.lang.String, com.demo2do.core.support.Page)
     */
    public List<Order> listByPage(List<Site> sites, boolean experience, boolean give, String orderStatus, Page page) {
        List<OrderStatus> status = OrderStatus.sameAs(orderStatus);
        
        if(status == null)
            status = Arrays.asList(OrderStatus.aliasOf(orderStatus));  
        
        List<Order> result = orderDao.listByPage(sites, OrderType.INDIVIDUAL, experience, give, status, page.getBeginIndex(), page.getMaxResultRecords());
        
        page.calculate(result.size());
        
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.ecommerce.OrderService#list(co.fitstart.common.entity.ecommerce.product.Product)
     */
    public List<Order> list(Product product) {
        
        List<OrderStatus> status = new ArrayList<OrderStatus>();
        status.add(OrderStatus.PENDING);
        status.add(OrderStatus.PROCESSING);
        
        return orderDao.list(product, status);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#list(boolean, boolean, co.fitstart.entity.order.PaymentStatus, com.demo2do.core.support.Page)
     */
    public List<Order> list(boolean exportSale, boolean payOffer, PaymentStatus[] paymentStatus, Page page) {
        Date date = DateUtils.parseDate("2017-12-01 00:00:00", "yyyy-MM-dd HH:mm:ss");
        List<Order> result = orderDao.list(exportSale, payOffer, paymentStatus, date, page.getBeginIndex(), page.getMaxResultRecords());
        
        page.calculate(result.size());
        
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;   
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#list(boolean, co.fitstart.entity.order.PaymentStatus[], com.demo2do.core.support.Page)
     */
    public List<Order> list(boolean exportReceipt, PaymentStatus[] paymentStatus, Page page) {
        Date date = DateUtils.parseDate("2017-12-01 00:00:00", "yyyy-MM-dd HH:mm:ss");
        List<Order> result = orderDao.list(exportReceipt, paymentStatus, date, page.getBeginIndex(), page.getMaxResultRecords());
        
        page.calculate(result.size());
        
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;   
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#list(boolean, co.fitstart.entity.order.PaymentStatus[], java.lang.String)
     */
    public List<Order> list(boolean payOffer, PaymentStatus[] paymentStatus, String city) {
        Date date = DateUtils.parseDate("2018-03-01 00:00:00", "yyyy-MM-dd HH:mm:ss");
        return orderDao.list(payOffer, paymentStatus, date, city);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#list(co.fitstart.entity.order.PaymentStatus[])
     */
    public List<Order> list(PaymentStatus[] paymentStatus) {
        Date date = DateUtils.parseDate("2017-12-01 00:00:00", "yyyy-MM-dd HH:mm:ss");
        return orderDao.list(paymentStatus, date);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.ecommerce.OrderService#loadOrder(java.lang.Long)
     */
    public Order loadOrder(Long id) {
        return genericDaoSupport.load(Order.class, id);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#createOnline(co.fitstart.entity.product.Product, co.fitstart.entity.reference.Contact, co.fitstart.entity.order.PaymentMethod)
     */
    public Order createOnline(Product product, Contact contact, PaymentMethod paymentMethod) {
        //1. 根据用户手机号查找用户
        String sentence = "FROM User user WHERE user.disabled = false AND user.mobile = :mobile";
        
        User user = genericDaoSupport.searchForObject(sentence, "mobile", contact.getContactPhone(), User.class);
        
        //2. 如果没有查询到该用户，抛出异常
        if(user == null){
            throw new BusinessException("用户不存在");
        }
        
        //3. update user real name 
        genericDaoSupport.update(user.editName(contact.getContactPerson()));
        
        //4. create a new unpaid order
        Order order = new Order(1);
        

        // 2 digits random number between 0 (inclusive) ~ 100 (exclusive)
        String randomNumber = String.format("%02d", Double.valueOf(Math.floor(Math.random() * 100)).intValue());
        
        // Generate code with 20 digits total
        String orderNo = new StringBuilder().append("W")
                                            .append(randomNumber)
                                            .append(DateFormatUtils.format(new Date(), "yyMMddHHmmssSSS"))
                                            .toString();
        
        order.initialize(orderNo, user, product)
             .addAttribute("createTime", DateFormatUtils.format(order.getCreateTime(), "yyyy-MM-dd HH:mm:SS"));
   
        // 5. init contact with person and phone
        order.addAttribute("contactPerson", contact.getContactPerson())
             .addAttribute("contactPhone", contact.getContactPhone());
        
        // 6. init order type
        Course course = product.getCourse();
        if(course == null) {
            order.initType(OrderType.OTHERS);
            
        }else if(course != null && course.isOnline()) {
            order.initType(OrderType.ONLINE);
            
        } else if(course != null && course.isYear()) {
            order.initType(OrderType.YEAR);
            
        } else if(course != null && course.isOffline() && course.isGroup()) {
            order.initType(OrderType.GROUP);
            
        } else {
            order.initType(OrderType.INDIVIDUAL);
        }
        
        // 7. save into database
        genericDaoSupport.save(order.calculatePrice());
        
        return order;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#editOrder(co.fitstart.entity.order.Order)
     */
    public void editOrder(Order order) {
        genericDaoSupport.update(order);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#editOrderPrice(co.fitstart.entity.order.Order, co.fitstart.entity.order.OrderPriceType, java.lang.String)
     */
    public void editOrderPrice(Order order, OrderPriceType type, String orderPriceRemarks) {
        if(order.getPaymentStatus().isPaid() && type.isSale()) {
            genericDaoSupport.update(order.editPaidPrice(order.getPrice()));
            
            RevenueReport revenueReport = reportDao.loadRevenueReport(order,order.isRefund() ? RevenueType.REFUND : RevenueType.PURCHASE);
            if(revenueReport != null) {
            	
            	genericDaoSupport.update(revenueReport.changePrice(order.getPrice()));
            }
            
            //edit order attr, orderPrice
            OrderAttribute price = orderDao.getOrderAttribute(order, "orderPrice");
            if(price != null) {
                genericDaoSupport.update(price.editValue(order.getPrice() + ""));
            }
            
            //edit order attr, paid_price
            OrderAttribute paidPrice = orderDao.getOrderAttribute(order, Constant.ORDER_PAID_PRICE);
            if(paidPrice != null) {
                genericDaoSupport.update(paidPrice.editValue(order.getPaidPrice() + ""));
            }
        } else {
            genericDaoSupport.update(order);
        }
        
        OrderAttribute orderAttribute = orderDao.getOrderAttribute(order, "priceRemarks");
        if(orderAttribute != null) {
            genericDaoSupport.update(orderAttribute.editValue(orderPriceRemarks));
        } else {
            orderAttribute = new OrderAttribute(order, "priceRemarks", orderPriceRemarks);
            genericDaoSupport.save(orderAttribute);
        }
        
        // save orderPriceAttribute
        OrderPriceAttribute orderPriceAttribute = new OrderPriceAttribute(type, order);
        genericDaoSupport.save(orderPriceAttribute);
        
        // update revenueReport if exist
        if(!order.getOrderType().isOnline()) {
            this.editRevenuePrice(order, type.isSale() ? RevenueType.PURCHASE : RevenueType.REFUND);
        }
        
    }
    
    /**
     * 
     * @param order
     * @param type
     */
    private void editRevenuePrice(Order order, RevenueType type) {
        RevenueReport report = reportDao.loadRevenueReport(order, type);
        if(report != null) {
            int offer = type.isPurchase() ? order.getPrice() : order.getRefundPrice();
            genericDaoSupport.update(report.editOffer(offer));
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#search(co.fitstart.admin.entity.search.OrderSearch, com.demo2do.core.support.Page)
     */
    @SuppressWarnings("unchecked")
    public List<Order> search(OrderSearch orderSearch, Page page) {
        String s = orderSearch.buildSentenceTemplate();
        Map<String, Object> parameters = orderSearch.buildParameters();
        List<Order> result = genericDaoSupport.searchForList(s, parameters, page.getBeginIndex(), page.getMaxResultRecords());
        page.calculate(result.size());
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<Order> search(AllOrderSearch orderSearch, Page page) {
        String s = orderSearch.buildSentenceTemplate();
        Map<String, Object> parameters = orderSearch.buildParameters();
        List<Order> result = genericDaoSupport.searchForList(s, parameters, page.getBeginIndex(), page.getMaxResultRecords());
        page.calculate(result.size());
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#search(co.fitstart.entity.order.OrderType, co.fitstart.entity.order.OrderStatus, java.lang.Long, com.demo2do.core.support.Page)
     */
    public List<Order> search(OrderType orderType, OrderStatus orderStatus, Long orderId, Page page) {
        List<Order> result = orderDao.list(orderId, orderType, orderStatus, page.getBeginIndex(), page.getMaxResultRecords());
        page.calculate(result.size());
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#search(boolean, boolean, co.fitstart.entity.order.OrderStatus, java.lang.Long, com.demo2do.core.support.Page)
     */
    public List<Order> search(boolean experience, boolean give, OrderStatus orderStatus, Long orderId, Page page) {
        List<Order> result = orderDao.list(orderId, OrderType.INDIVIDUAL, experience, give, orderStatus, page.getBeginIndex(), page.getMaxResultRecords());
        page.calculate(result.size());
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#exportOrder(co.fitstart.admin.entity.search.OrderSearch)
     */
    @SuppressWarnings("unchecked")
    public List<Order> exportOrder(OrderSearch orderSearch) {
        String sentence = orderSearch.buildSentenceTemplate();
        Map<String, Object> param = orderSearch.buildParameters();
        return genericDaoSupport.searchForList(sentence, param);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#totalOrder(co.fitstart.admin.entity.search.OrderSearch)
     */
    public int totalOrder(OrderSearch orderSearch) {
        return genericDaoSupport.count(orderSearch.buildSentenceTemplate(), orderSearch.buildParameters());
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#totalOrder(co.fitstart.admin.entity.search.AllOrderSearch)
     */
    public int totalOrder(AllOrderSearch orderSearch) {
        return genericDaoSupport.count(orderSearch.buildSentenceTemplate(), orderSearch.buildParameters());
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#changeStatus(co.fitstart.entity.order.Order, co.fitstart.entity.order.OrderStatus, co.fitstart.entity.order.PaymentStatus)
     */
    public void changeStatus(Order order, OrderStatus orderStatus, PaymentStatus paymentStatus) {
        order.orderStatus(orderStatus);
        genericDaoSupport.update(order.paymentStatus(paymentStatus));
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#ship(co.fitstart.entity.order.Order, java.lang.String, java.lang.String)
     */
    public void ship(Order order, String expressageCompany, String expressageNumber) {
        // add order expressageCompany and expressageNumber to orderAttribute
        order.addAttribute(Constant.SHIPPING_EXPRESSAGE_COMPANY, expressageCompany);
        order.addAttribute(Constant.SHIPPING_EXPRESSAGE_NUMBER, expressageNumber);
        
        // update order status first
        genericDaoSupport.update(order.orderStatus(OrderStatus.SHIPPED));
        
    }
    
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#close(co.fitstart.entity.order.Order, co.fitstart.entity.order.OrderStatus, co.fitstart.entity.order.PaymentStatus)
     */
    public void close(Order order) {
        genericDaoSupport.update(order.cancel());
        
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        OrderAttribute orderAttribute = new OrderAttribute(order, "close_time", format.format(new Date()));
        genericDaoSupport.save(orderAttribute);
        
        if(order.getAttrs() != null && order.getAttrs().get("couponId") != null && org.apache.commons.lang3.StringUtils.isNotBlank(order.getAttrs().get("couponId").getValue())) {
            Long couponId = Long.parseLong(order.getAttrs().get("couponId").getValue());
            Coupon coupon = genericDaoSupport.load(Coupon.class, couponId);
            
            if(DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"), "yyyy-MM-dd").after(coupon.getEffectiveEndDate())) {
                genericDaoSupport.update(coupon.expiry());
            } else {
                genericDaoSupport.update(coupon.claimed());
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#refundOrder(co.fitstart.entity.order.Order, java.math.BigDecimal, java.lang.String)
     */
    public void refundOrder(Order order, BigDecimal refundPrice, RefundMethod refundMethod, String refoundRemarks) {
        
        this.refundOrderBase(order, refundPrice, refundMethod);
        
        if(order.getOrderType().isOnline()) {
            this.removeOnlineOrdeerSource(order);
        }
        
        OrderAttribute orderAttribute = orderDao.getOrderAttribute(order, "refoundRemarks");
        if(orderAttribute != null) {
            genericDaoSupport.update(orderAttribute.editValue(refoundRemarks));
        } else {
            orderAttribute = new OrderAttribute(order, "refoundRemarks", refoundRemarks);
            genericDaoSupport.save(orderAttribute);
        }
    }
    
    /**
     * 
     * @param order
     */
    private void removeOnlineOrdeerSource(Order order) {
        
        // 1 close course round
        CourseRoundAttribute courseRoundAttribute = courseRoundDao.loadCourseRoundAttribute(Constant.COURSER_ROUND_ORDER, order.getId().toString());
        
        if(courseRoundAttribute != null) {
            
            //TODO remove order investigation result folder
            CourseRound courseRound = courseRoundAttribute.getCourseRound();
            
            OrderInvestigateRecord orderInvestigateRecord = orderDao.loadOrderInvestigateRecord(order);
            
            if(orderInvestigateRecord != null && orderInvestigateRecord.getFitInvestigation() != null) {
                String filename = org.apache.commons.lang3.StringUtils.replace(order.getProduct().getName(), " ", "-");
                // save image
                String filePath = filename + "/" +
                                  "[" + order.getOrderStatus().getDescription() + "-" + order.getPaymentStatus().getDescription() + "]" + filename + "_" + 
                                  order.getStudent().getUser().getProfile().getNickname() + "_" + order.getStudent().getUser().getWechat().getCode() + "_" + 
                                  orderInvestigateRecord.getFitInvestigation().getHeight() + "_" + orderInvestigateRecord.getFitInvestigation().getWeight() + "_" + orderInvestigateRecord.getFitInvestigation().getBmiValue();
                
                File directory = FileUtils.getFile(uploadPath + filePath);
                try {
                    if(directory.exists()) {
                        FileUtils.cleanDirectory(directory);
                        FileUtils.deleteDirectory(directory);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            
            genericDaoSupport.update(courseRound.changeStatus(RoundStatus.CANCEL));
        }
    }
    
    /**
     * Refound order --- (set order status as canceled & payment status as system refunded) 
     * @param order
     */
    private void refundOrderBase(Order order, BigDecimal refundPrice, RefundMethod refundMethod) {
        
        genericDaoSupport.update(order.refund(refundPrice, refundMethod));
   
        OrderAttribute orderAttribute = new OrderAttribute(order, "refund_time", DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        genericDaoSupport.save(orderAttribute);
        
        if(order.getOrderType().isOffline()) {
            CourseRound courseRound = courseRoundDao.loadCourseRound(order);
            genericDaoSupport.update(courseRound.changeStatus(RoundStatus.CANCEL));
            
            // update courseSchedule's status
            courseScheduleDao.updateScheduleStatus(courseRound, ScheduleStatus.FINISH);
            
            // delete purchase report if exist
            /*RevenueReport revenueReport = reportDao.loadRevenueReport(courseRound);
            if(revenueReport != null) {
                genericDaoSupport.update(revenueReport.disable());
            }*/
            
            //send message to student
            String mobile = courseRound.getStudent().getMobile();
            String site = courseRound.getSite().getName();
            
            if(courseRound.getCourseType().isYear()) {
                smsSender.sendYearCancelNotice(mobile, site);
            } else if((courseRound.getCourseType().isIndividual() || courseRound.getCourseType().isSpecial() || courseRound.getCourseType().isPosture()) && !courseRound.getCourse().isExperience()) {
                int courseAmount = courseRound.getCourseAmount();
                
                smsSender.sendIndividualCancelNotice(mobile, site, courseAmount);
            }
        }
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#changeSalesman(co.fitstart.entity.order.Order, co.fitstart.entity.principal.Principal)
     */
    public void changeSalesman(Order order, Principal salesman) {    	
        genericDaoSupport.update(order.changeSalesman(salesman));
        RevenueReport revenueReport = reportDao.loadRevenueReport(order,order.isRefund() ? RevenueType.REFUND : RevenueType.PURCHASE);
        if(revenueReport != null) {
        	
        	genericDaoSupport.update(revenueReport.changeSaleman(salesman.getName()));
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#createExperienceOrder(co.fitstart.entity.product.Product, java.lang.String, java.lang.String, co.fitstart.entity.site.Site)
     */
    public Order createExperienceOrder(Product product, String mobile, String name, Site site, Long userSourceType) {
        
        // 1. Find the User according to his mobile or email
        String sentence = "FROM User user WHERE user.disabled = false AND user.mobile = :mobile";
        User user = genericDaoSupport.searchForObject(sentence, "mobile", mobile, User.class);
        
        //  If there is no such user, then create the user and userAccount
        if(user == null) {
            user = userHandler.create(mobile, name, false, Constants.USER_ACCOUNT_MOBILE_KEY, mobile);
        }
        
        if(user.getName() == null || StringUtils.isEmpty(user.getName())) {
            user.setName(name);
            genericDaoSupport.update(user);
        }
        
        //2. Create new Order
        Order order = new Order(1);
        
        //  two digits random number between 0 (inclusive) ~ 100 (exclusive)
        String randomNumber = String.format("%02d", Double.valueOf(Math.floor(Math.random() * 100)).intValue());
        
        //  Generate code with 20 digits total
        String orderNo = new StringBuilder().append("W")
                                            .append(randomNumber)
                                            .append(DateFormatUtils.format(new Date(), "yyMMddHHmmssSSS"))
                                            .toString();
        
        //3. Initialize this order
        order.initializeExperience(orderNo, user, product)
            .addAttribute("createTime", DateFormatUtils.format(order.getCreateTime(), "yyyy-MM-dd HH:mm:SS"));
        
        //4. Keep buyer info and bind with order
        order.addAttribute("contactPerson", name)
            .addAttribute("contactPhone", mobile);
        
        order.site(site);
        order.addAttribute("siteId", site.getId());
        
        //5. initialize order type
        Course course = product.getCourse();
        order.initType(OrderType.determineTo(course));
                
        //6. calculate price
        if(site.getId().equals(WXC_ID)) {
            order.calculateWxcPrice();
        } else {
            order.calculatePrice();
        }
        
        if(userSourceType != null) {
        	UserSourceType userSourceTypeRal = genericDaoSupport.load(UserSourceType.class, userSourceType);
            if(userSourceTypeRal != null) {
            	order.setUserSourceType(userSourceTypeRal);
            }
        }
        
        //7. save this order to database
        genericDaoSupport.save(order);
        
        return order;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#invoice(co.fitstart.entity.finance.OrderInvoiceRecord, co.fitstart.entity.order.Order, java.lang.Long)
     */
    public void invoice(OrderInvoiceRecord orderInvoiceRecord, Order order, Long operator) {
        genericDaoSupport.save(orderInvoiceRecord.initilization(order, operator));
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#exportSales(java.util.List, java.lang.Long)
     */
    public void exportSales(List<Long> orderIds, Long operator) {
        // update order's exportSales 
        orderDao.exportSales(orderIds);
        
        // save sales order export record
        SalesOrderExportRecord record = new SalesOrderExportRecord(orderIds.size(), operator);
        genericDaoSupport.save(record);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#exportReceipt(java.util.List, co.fitstart.entity.finance.ReceiptType, java.lang.Long)
     */
    public void exportReceipt(List<Long> orderIds, ReceiptType type, Long operator) {
        // update order's exportReceipt 
        orderDao.exportReceipt(orderIds);
        
        // save sales order export record
        ReceiptExportRecord record = new ReceiptExportRecord(orderIds.size(), type, operator);
        genericDaoSupport.save(record);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listSalesOrderExportRecord(com.demo2do.core.support.Page)
     */
    public List<SalesOrderExportRecord> listSalesOrderExportRecord(Page page) {
        List<SalesOrderExportRecord> result = orderDao.listSaleExportRecord(page.getBeginIndex(), page.getMaxResultRecords());
        
        page.calculate(result.size());
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listReceiptExportRecord(com.demo2do.core.support.Page)
     */
    public List<ReceiptExportRecord> listReceiptExportRecord(Page page) {
        List<ReceiptExportRecord> result = orderDao.listReceiptExportRecord(page.getBeginIndex(), page.getMaxResultRecords());
        
        page.calculate(result.size());
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listOrderInvoiceRecord(boolean, boolean, com.demo2do.core.support.Page)
     */
    public List<OrderInvoiceRecord> listOrderInvoiceRecord(boolean invoice, boolean export, Page page) {
        Date date = DateUtils.parseDate("2017-12-01 00:00:00", "yyyy-MM-dd HH:mm:ss");
        List<OrderInvoiceRecord> result = orderDao.listOrderInvoiceRecord(invoice, export, date, page.getBeginIndex(), page.getMaxResultRecords());
        
        page.calculate(result.size());
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listOrderInvoiceRecord()
     */
    public List<OrderInvoiceRecord> listOrderInvoiceRecord() {
        Date date = DateUtils.parseDate("2018-03-01 00:00:00", "yyyy-MM-dd HH:mm:ss");
        return orderDao.listOrderInvoiceRecord(date);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#exportInvoiceRecord(java.util.List, java.lang.Long)
     */
    public void exportInvoiceRecord(List<Long> invoiceRecordIds, Long operator) {
        // update order's exportReceipt 
        orderDao.exportOrderInvoiceRecord(invoiceRecordIds);
        
        // save sales order export record
        InvoiceExportRecord record = new InvoiceExportRecord(invoiceRecordIds.size(), operator);
        genericDaoSupport.save(record);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listInvoiceExportRecord(com.demo2do.core.support.Page)
     */
    public List<InvoiceExportRecord> listInvoiceExportRecord(Page page) {
        List<InvoiceExportRecord> result = orderDao.listInvoiceRecordExportRecord(page.getBeginIndex(), page.getMaxResultRecords());
        
        page.calculate(result.size());
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#editOrderNumber(co.fitstart.entity.order.Order, java.lang.String)
     */
    public void editOrderNumber(Order order, String orderNumber) {
        order.addAttribute(Constant.ONLINE_ORDER_NUMBER, orderNumber);
        genericDaoSupport.update(order);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#deposit(co.fitstart.entity.order.Order, java.math.BigDecimal, co.fitstart.entity.order.PaymentMethod)
     */
    public void deposit(Order order, BigDecimal deposit, PaymentMethod paymentMethod) {
        // update order's deposit and paid price
        genericDaoSupport.update(order.deposit(deposit));
        
        // create receipt
        List<ExchangeRate> exchangeRates = genericDaoSupport.loadAll(ExchangeRate.class);
        BigDecimal exchangeRate = exchangeRates.isEmpty() ? null : exchangeRates.get(0).getExchangeRate();
        String entryPerson = this.getEntryPerson(order.getSite().getId());
        
        Receipt receipt = new Receipt(order, ReceiptType.DEPOSIT, PaidType.DEPOSIT, paymentMethod, exchangeRate, deposit.multiply(new BigDecimal(100)).intValue(), entryPerson);
        genericDaoSupport.save(receipt);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#offer(co.fitstart.entity.order.Order, java.math.BigDecimal, co.fitstart.entity.order.PaymentMethod)
     */
    public void offer(Order order, BigDecimal offer, PaymentMethod paymentMethod) {
        
        if(order.isPayOffer()) {
            // if this order has paid offer update paidPrice
            genericDaoSupport.update(order.editPaidPrice(offer));
        } else {
            // update order's offer and payOffer
            genericDaoSupport.update(order.payOffer(offer));
        }
        
        // create balance receipt
        String entryPerson = this.getEntryPerson(order.getSite().getId());
        
        Receipt receipt = new Receipt(order, ReceiptType.BALANCE, PaidType.BALANCE, paymentMethod, new BigDecimal(1), offer.multiply(new BigDecimal(100)).intValue(), entryPerson);
        genericDaoSupport.save(receipt);
    }
    
    /**
     * 
     * @return
     */
    private String getEntryPerson(Long siteId) {
        List<PrincipalReportRelation> relations = principalDao.listSiteManager();
        for(PrincipalReportRelation relation : relations) {
            if(relation.getSite().getId() == siteId) {
                return relation.getManager().getName();
            }
        }
        return null;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#payBalance(co.fitstart.entity.order.Order, java.math.BigDecimal, co.fitstart.entity.order.PaymentMethod)
     */
    public void payBalance(Order order, BigDecimal balance, PaymentMethod paymentMethod) {
        // update order's paid price
        genericDaoSupport.update(order.payBalance(balance));
        
        // create receipt
        List<ExchangeRate> exchangeRates = genericDaoSupport.loadAll(ExchangeRate.class);
        BigDecimal exchangeRate = exchangeRates.isEmpty() ? null : exchangeRates.get(0).getExchangeRate();
        String entryPerson = this.getEntryPerson(order.getSite().getId());
        
        Receipt receipt = new Receipt(order, ReceiptType.BALANCE, PaidType.BALANCE, paymentMethod, exchangeRate, balance.multiply(new BigDecimal(100)).intValue(), entryPerson);
        genericDaoSupport.save(receipt);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listReceipt(co.fitstart.entity.finance.PaidType, boolean, com.demo2do.core.support.Page)
     */
    public List<Receipt> listReceipt(PaidType paidType, boolean export, Page page) {
        Date date = DateUtils.parseDate("2017-12-01 00:00:00", "yyyy-MM-dd HH:mm:ss");
        List<Receipt> result = orderDao.listReceipt(export, paidType, date, page.getBeginIndex(), page.getMaxResultRecords());
        
        page.calculate(result.size());
        
        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listReceipt(co.fitstart.entity.finance.PaidType, boolean)
     */
    public List<Receipt> listReceipt(PaidType paidType, boolean export) {
        Date date = DateUtils.parseDate("2017-12-01 00:00:00", "yyyy-MM-dd HH:mm:ss");
        return orderDao.listReceipt(export, paidType, date);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#exportSaleReceipt(java.util.List, co.fitstart.entity.finance.ReceiptType, java.lang.Long)
     */
    public void exportSaleReceipt(List<Long> receiptIds, ReceiptType receiptType, Long operator) {
        orderDao.exportSaleReceipt(receiptIds);
        
        // save sales order export record
        ReceiptExportRecord record = new ReceiptExportRecord(receiptIds.size(), receiptType, operator);
        genericDaoSupport.save(record);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#getOrderId(java.lang.String)
     */
    public Long getOrderId(String orderNumber) {
        return orderDao.getOrderId(Constant.ONLINE_ORDER_NUMBER, orderNumber);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#validateUserExperienceCount(java.lang.String)
     */
    public void validateUserExperienceCount(Product product, String expUserMobile) {
        if(expUserMobile != null) {
            User user = userDao.load(expUserMobile);
            
            if(user != null) {
            	if(product.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_EXPERIENCE)) {
            		String sentence = "FROM Order WHERE user = :user AND orderType = :individual "
                            + "AND course.experience = true AND course.id = :courseId AND orderStatus in (:orderStatus) "
                            + "AND paymentStatus in (:paymentStatus) AND disabled = false";

                    Map<String, Object> parameters = new HashMap<String, Object>();
                    parameters.put("user", user);
                    parameters.put("individual", OrderType.INDIVIDUAL);
                    parameters.put("courseId", product.getCourse().getId());
                    parameters.put("orderStatus", new OrderStatus[]{OrderStatus.CREATED, OrderStatus.PENDING, OrderStatus.PROCESSING, OrderStatus.COMPLETE});
                    parameters.put("paymentStatus", new PaymentStatus[]{PaymentStatus.UNPAID, PaymentStatus.PAID});

                    if(genericDaoSupport.count(sentence, parameters) > 1) {
                        throw new BusinessException("同一用户最多可上两次常规体验课");
                    }
            	} else if(product.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_WEEK_EXPERIENCE) || 
            			product.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_EXPERIENCE) || 
            			product.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_WEEK_EXPERIENCE)) {
            		
            		String sentence = "FROM Order WHERE user = :user AND orderType = :individual "
                            + "AND course.experience = true AND course.id = :courseId AND orderStatus in (:orderStatus) "
                            + "AND paymentStatus in (:paymentStatus) AND disabled = false";

                    Map<String, Object> parameters = new HashMap<String, Object>();
                    parameters.put("user", user);
                    parameters.put("individual", OrderType.INDIVIDUAL);
                    parameters.put("courseId", product.getCourse().getId());
                    parameters.put("orderStatus", new OrderStatus[]{OrderStatus.CREATED, OrderStatus.PENDING, OrderStatus.PROCESSING, OrderStatus.COMPLETE});
                    parameters.put("paymentStatus", new PaymentStatus[]{PaymentStatus.UNPAID, PaymentStatus.PAID});
                    
                    if(genericDaoSupport.count(sentence, parameters) > 0) {
                        throw new BusinessException("同一用户最多可上一次"+product.getCourse().getName());
                    }
            		
            	}
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#editUserName(co.fitstart.entity.order.Order, java.lang.String)
     */
    public void editUserName(Order order, String userName) {
        User user = order.getUser();
        genericDaoSupport.update(user.editName(userName));
        
        Student student = order.getStudent();
        if(student != null) {
            genericDaoSupport.update(student.editName(userName));
        }
        
        RevenueReport revenueReport = reportDao.loadRevenueReport(order,order.isRefund() ? RevenueType.REFUND : RevenueType.PURCHASE );
        if(revenueReport != null) {
        	
        	genericDaoSupport.update(revenueReport.changeUsername(userName));
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#editMobile(co.fitstart.entity.order.Order, java.lang.String)
     */
    public void editMobile(Order order, String mobile) {
        if(userDao.load(mobile) == null) {
            User user = order.getUser();
            genericDaoSupport.update(user.editMobile(mobile));
            
            Student student = order.getStudent();
            if(student != null) {
                genericDaoSupport.update(student.editMobile(mobile));
                
                RevenueReport revenueReport = reportDao.loadRevenueReport(order,order.isRefund() ? RevenueType.REFUND : RevenueType.PURCHASE );
                
                if(revenueReport != null) {
                	
                	genericDaoSupport.update(revenueReport.changeUserMobile(mobile));
                }
            }
        } else {
            throw new BusinessException("已存在手机号为" + mobile +"的用户");
        }
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#listReceipt(co.fitstart.entity.order.Order)
     */
    @SuppressWarnings("unchecked")
    public List<Receipt> listReceipt(Order order) {
        if(order.getCode() == null) {
            return null;
        } else {
            return genericDaoSupport.searchForList("FROM Receipt WHERE code = :code", "code", order.getCode());
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#createGive(java.lang.String, co.fitstart.entity.product.Product, co.fitstart.entity.site.Site)
     */
    public Order createGive(String mobile, String name, Product product, Site site, Long userSourceType) {
        
        if(product.getCourse() == null || !product.getCourse().isGive()) {
            throw new BusinessException("该产品不是赠课");
        }
        
        User user = userDao.load(mobile.trim());
        // create user here
        // get student, if student doesnt exist make one too.
        
        if(user == null){
            user = new User(mobile.trim(), name, false);
            genericDaoSupport.save(user);
            // subscribed
        }

        if(user !=null && user.getName() == null ){
            genericDaoSupport.update(user.editName(name));
        }
        
        //4. create a new unpaid order
        Order order = new Order(1);
        

        // 2 digits random number between 0 (inclusive) ~ 100 (exclusive)
        String randomNumber = String.format("%02d", Double.valueOf(Math.floor(Math.random() * 100)).intValue());
        
        // Generate code with 20 digits total
        String orderNo = new StringBuilder().append("W")
                                            .append(randomNumber)
                                            .append(DateFormatUtils.format(new Date(), "yyMMddHHmmssSSS"))
                                            .toString();
        
        order.initialize(orderNo, user, product, site)
             .addAttribute("createTime", DateFormatUtils.format(order.getCreateTime(), "yyyy-MM-dd HH:mm:SS"));
   
        // 5. init contact with person and phone
        order.addAttribute("contactPerson", user.getName())
             .addAttribute("contactPhone", user.getMobile());
        
        // 6. init order type
        order.initType(OrderType.INDIVIDUAL);
        
        if(userSourceType != null) {
        	UserSourceType userSourceTypeRal = genericDaoSupport.load(UserSourceType.class, userSourceType);
            if(userSourceTypeRal != null) {
            	order.setUserSourceType(userSourceTypeRal);
            }
        }
        
        // 7. save into database
        genericDaoSupport.save(order.calculateGivePrice());
        
        return order;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.OrderService#createExperience(java.lang.String, co.fitstart.entity.product.Product, co.fitstart.entity.site.Site)
     */
    public Order createExperience(String mobile, String name, Product product, Site site, Long userSourceType) {
    	
        if(product.getCourse() == null || !product.getCourse().isExperience()) {
           throw new BusinessException("该产品不是体验课");
        }
        
        User user = userDao.load(mobile.trim());
        // create user here
        // get student, if student doesnt exist make one too.
        
        if(user == null){
            user = new User(mobile.trim(), name, false);
            genericDaoSupport.save(user);
            // subscribed
        }

        if(user !=null && user.getName() == null ){
            genericDaoSupport.update(user.editName(name));
        }
        
        //4. create a new unpaid order
        Order order = new Order(1);
        

        // 2 digits random number between 0 (inclusive) ~ 100 (exclusive)
        String randomNumber = String.format("%02d", Double.valueOf(Math.floor(Math.random() * 100)).intValue());
        
        // Generate code with 20 digits total
        String orderNo = new StringBuilder().append("E")
                                            .append(randomNumber)
                                            .append(DateFormatUtils.format(new Date(), "yyMMddHHmmssSSS"))
                                            .toString();
        
        order.initialize(orderNo, user, product, site)
             .addAttribute("createTime", DateFormatUtils.format(order.getCreateTime(), "yyyy-MM-dd HH:mm:SS"));
   
        // 5. init contact with person and phone
        order.addAttribute("contactPerson", user.getName())
             .addAttribute("contactPhone", user.getMobile());
        
        // 6. init order type
        order.initType(OrderType.INDIVIDUAL);
        
        if(userSourceType != null) {
        	UserSourceType userSourceTypeRal = genericDaoSupport.load(UserSourceType.class, userSourceType);
            if(userSourceTypeRal != null) {
            	order.setUserSourceType(userSourceTypeRal);
            }
        }

        // 7. save into database
        if(product.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_EXPERIENCE) || product.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_WEEK_EXPERIENCE)) {
            genericDaoSupport.save(order.calculateExperienceCouponPrice());
        } else {
            genericDaoSupport.save(order.calculatePrice());
        }
        
        
        return order;
    }
    
    public Order createOrderIn(String mobile, String name, Product product, Site site, Coupon coupon, Long userSourceType, String courseAttr) {
        
        User user = userDao.load(mobile.trim());
        // create user here
        // get student, if student doesnt exist make one too.
        
        if(user == null){
            user = new User(mobile.trim(), name, false);
            genericDaoSupport.save(user);
            // subscribed
        }

        if(user !=null && user.getName() == null ){
            genericDaoSupport.update(user.editName(name));
        }

        Order order = new Order(1);

        String randomNumber = String.format("%02d", Double.valueOf(Math.floor(Math.random() * 100)).intValue());

        String orderNo = new StringBuilder().append("W")
                .append(randomNumber)
                .append(DateFormatUtils.format(new Date(), "yyMMddHHmmssSSS"))
                .toString();

        order.initialize(orderNo, user, product, site)
                .addAttribute("createTime", DateFormatUtils.format(order.getCreateTime(), "yyyy-MM-dd HH:mm:SS"));

        order.addAttribute("contactPerson", user.getName())
                .addAttribute("contactPhone", user.getMobile());

        if(product.getCourseUnit() == null) {
            order.initType(OrderType.INDIVIDUAL);
        }else if(product.getCourseUnit().equals(CourseUnit.NORMAL_INDIVIDUAL) 
                || product.getCourseUnit().equals(CourseUnit.OTHER_INDIVIDUAL)
                || product.getCourseUnit().equals(CourseUnit.FAST_INDIVIDUAL)
                || product.getCourseUnit().equals(CourseUnit.YOUNG_INDIVIDUAL)
                || product.getCourseUnit().equals(CourseUnit.SHAPE_INDIVIDUAL)
                || product.getCourseUnit().equals(CourseUnit.NEW_OTHER_INDIVIDUAL)) {
            order.initType(OrderType.INDIVIDUAL);
        } else if(product.getCourseType().equals(CourseType.SPECIAL)) {
            order.initType(OrderType.SPECIAL);
        } else if(product.getCourseType().equals(CourseType.POSTURE)) {
            order.initType(OrderType.POSTURE);
        }
        
        if(userSourceType != null) {
        	UserSourceType userSourceTypeRal = genericDaoSupport.load(UserSourceType.class, userSourceType);
            if(userSourceTypeRal != null) {
            	order.setUserSourceType(userSourceTypeRal);
            }
        }
        
        if(org.apache.commons.lang3.StringUtils.isNotEmpty(courseAttr)) {
        	order.setCourseTags(courseAttr);
        }
        
        if(coupon != null) {
            
            if(coupon.getUserId() != user.getId()) {
                throw new BusinessException("500", "优惠券所属用户不匹配");
            }
            
            order.calculatePrice(coupon);
            order.addAttribute("couponId", coupon.getId());
            order.addAttribute("priceRemarks", coupon.getBatchName() + (coupon.getTemplate().getDiscountType().isPercent() ? "抵扣全额" : (coupon.getTemplate().getDiscountType().isFixedAmount() ? ("抵扣" + coupon.getAmount()/100 + "元") : "")));
            
            genericDaoSupport.update(coupon.occupy());
            
        } else {
            order.calculatePrice();
        }

        genericDaoSupport.save(order);

        return order;
    }
    
    public Order createOrderYear(String mobile, String name, Product product, Site site, Coupon coupon, Long userSourceType) {
        User user = userDao.load(mobile.trim());
        // create user here
        // get student, if student doesnt exist make one too.
        
        if(user == null){
            user = new User(mobile.trim(), name, false);
            genericDaoSupport.save(user);
            // subscribed
        }

        if(user !=null && user.getName() == null ){

            genericDaoSupport.update(user.editName(name));
        }
        
        

        Order order = new Order(1);


        String randomNumber = String.format("%02d", Double.valueOf(Math.floor(Math.random() * 100)).intValue());

        String orderNo = new StringBuilder().append("W")
                .append(randomNumber)
                .append(DateFormatUtils.format(new Date(), "yyMMddHHmmssSSS"))
                .toString();

        order.initialize(orderNo, user, product, site)
                .addAttribute("createTime", DateFormatUtils.format(order.getCreateTime(), "yyyy-MM-dd HH:mm:SS"));

        order.addAttribute("contactPerson", user.getName())
                .addAttribute("contactPhone", user.getMobile());


        order.initType(OrderType.YEAR);
        
        if(userSourceType != null) {
        	UserSourceType userSourceTypeRal = genericDaoSupport.load(UserSourceType.class, userSourceType);
            if(userSourceTypeRal != null) {
            	order.setUserSourceType(userSourceTypeRal);
            }
        }
        
        if(coupon != null) {
            
            if(coupon.getUserId() != user.getId()) {
                throw new BusinessException("500", "优惠券所属用户不匹配");
            }
            
            order.calculatePrice(coupon);
            
            order.addAttribute("couponId", coupon.getId());
            order.addAttribute("priceRemarks", coupon.getBatchName() + (coupon.getTemplate().getDiscountType().isPercent() ? "抵扣全额" : (coupon.getTemplate().getDiscountType().isFixedAmount() ? ("抵扣" + coupon.getAmount()/100 + "元") : "")));
            
            genericDaoSupport.update(coupon.occupy());
            
        } else {
            order.calculatePrice();
        }

        genericDaoSupport.save(order);

        return order;
    }
    
    @Override
    public Order createOrderCard(String mobile, String name, Product product, Site site, Coupon coupon,
    		Long userSourceType) {
    	
    	User user = userDao.load(mobile.trim());
        // create user here
        // get student, if student doesnt exist make one too.
        
        if(user == null){
            user = new User(mobile.trim(), name, false);
            genericDaoSupport.save(user);
            // subscribed
        }

        if(user !=null && user.getName() == null ){
            genericDaoSupport.update(user.editName(name));
        }

        Order order = new Order(1);
        String randomNumber = String.format("%02d", Double.valueOf(Math.floor(Math.random() * 100)).intValue());

        String orderNo = new StringBuilder().append("H")
                .append(randomNumber)
                .append(DateFormatUtils.format(new Date(), "yyMMddHHmmssSSS"))
                .toString();

        order.initialize(orderNo, user, product, site)
                .addAttribute("createTime", DateFormatUtils.format(order.getCreateTime(), "yyyy-MM-dd HH:mm:SS"));

        order.addAttribute("contactPerson", user.getName())
                .addAttribute("contactPhone", user.getMobile());


        order.initType(OrderType.CARD);
        
        if(userSourceType != null) {
        	UserSourceType userSourceTypeRal = genericDaoSupport.load(UserSourceType.class, userSourceType);
            if(userSourceTypeRal != null) {
            	order.setUserSourceType(userSourceTypeRal);
            }
        }
        
        if(coupon != null) {
            
            if(coupon.getUserId() != user.getId()) {
                throw new BusinessException("500", "优惠券所属用户不匹配");
            }
            
            order.calculatePrice(coupon);
            
            order.addAttribute("couponId", coupon.getId());
            order.addAttribute("priceRemarks", coupon.getBatchName() + (coupon.getTemplate().getDiscountType().isPercent() ? "抵扣全额" : (coupon.getTemplate().getDiscountType().isFixedAmount() ? ("抵扣" + coupon.getAmount()/100 + "元") : "")));
            
            genericDaoSupport.update(coupon.occupy());
            
        } else {
            order.calculatePrice();
        }

        genericDaoSupport.save(order);

        return order;
    }
}
